/**
 *  TextMash - simple IDE for Clojure
 *  
 *  Copyright (C) 2010 Aleksander Naszko
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package pl.olek.jruce;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * @author anaszko
 */
public class Watchdog<T> {

//    final private static Logger logger = Logger.getLogger(Watchdog.class.getName());
    TreeMap<Long, LinkedList<T>> reported = new TreeMap<Long, LinkedList<T>>();

    TreeMap<T, Long> alive = new TreeMap<T, Long>();

    int dead;

    public synchronized void clear() {
        dead = 0;
        reported.clear();
        alive.clear();
    }

    public synchronized boolean report(Long timestamp, T object) {



//        logger.info("Registering " + object);

        LinkedList<T> collection = reported.get(timestamp);
        if (collection == null) {
            collection = new LinkedList<T>();
            reported.put(timestamp, collection);
        }

        Long old = alive.remove(object);

        Long oldTimestamp = alive.put(object, timestamp);
        if (oldTimestamp != null) {
            LinkedList<T> list = reported.get(oldTimestamp);
            if (list.remove(object)) {
                if (list.isEmpty() && !oldTimestamp.equals(timestamp)) {
                    reported.remove(oldTimestamp);
                }
            }
        }

        collection.add(object);

        return old == null;
    }

    public synchronized int getDeadCount() {
        return dead;
    }

    public synchronized int getAliveCount() {
        return alive.size();
    }

    public synchronized Collection<T> getAlive() {
        return new TreeSet<T>(alive.keySet());
    }

    public synchronized Set<T> getTimeouted(Long timestamp) {
        HashSet<T> values = new HashSet<T>();
        SortedMap<Long, LinkedList<T>> tail = reported.tailMap(timestamp);
        for (LinkedList<T> objects : tail.values()) {
            for (T object : objects) {
                values.add(object);
            }
        }
        SortedMap<Long, LinkedList<T>> head = reported.headMap(timestamp);
        TreeSet<T> result = new TreeSet<T>();
        for (LinkedList<T> objects : head.values()) {
            Iterator<T> objectIterator = objects.iterator();
            while (objectIterator.hasNext()) {
                T object = objectIterator.next();
                if (!values.contains(object)) {
                    objectIterator.remove();
                    result.add(object);
                    alive.remove(object);
                }
            }
        }
        head.clear();
        dead += result.size();
        return result;
    }

    @Override
    public String toString() {
        return alive.toString();
    }
}
